/*
PARI/GP scripts related to the Lambert W function
=============================================================
File: LambertWFunctions.txt    
Link: https://oeis.org/wiki/File:LambertWFunctions.txt
=============================================================
*/


LambertW0(x) =
/* ----------------------------------------------------------
 Computes the upper branch of the Lambert W function.
 Intended for t_REAL arguments.
 Domain: [-1/e, +inf), Image: [-1, +inf)

 Notes: the function lambertw(x) built-in PARI (Version 2.6.1)
 works only for x >= 0, but does not cover the double-valued
 part of W for -1/e <= x < 0. This script defaults on the PARI
 version whenever possible but, together with LambertW1, adds
 a brute-force solution (albeit not very efficient,
 I am afraid) for the rest.
---------------------------------------------------------- */
{
  my(c,eps);
  c = x + 0.0;
  eps = 10^(-default(realprecision)+1);
  if (type(c)!="t_REAL", error("Argument incompatible with t_REAL"));
  if (c >=0, return (lambertw(c)));
  if (c < -1.0/exp(1.0), error("Argument must be >= -1/e"));
  return (-solve(y=-eps,1.0+eps,-c*exp(y)-y));
}

LambertW1(x) =
/* ----------------------------------------------------------
 Computes the lower branch of the Lambert W function.
 Intended for t_REAL arguments.
 Domain: [-1/e, 0), Image: (-1, -inf]

 Notes: the function lambertw(x) built-in PARI (Version 2.6.1)
 works only for x >= 0, but does not cover the double-valued
 part of W for -1/e <= x < 0. This script, together with
 LambertW0, adds a brute-force solution (albeit not very
 efficient, I am afraid) for the rest.
---------------------------------------------------------- */
{
  my(c,eps,ulim);
  c = x + 0.0;
  eps = 10^(-default(realprecision)+1);
  if (type(c)!="t_REAL", error("Argument incompatible with t_REAL"));
  if (c >=0, error("Argument must be < 0"));
  if (c < -1.0/exp(1), error("Argument must be >= -1/e"));
  ulim = 10.0;
  while (1,
    if (-c*exp(ulim)-ulim > 0.0, break);
    ulim *= 2.0;
  );
  return (-solve(y=1.0-eps,ulim,-c*exp(y)-y));
}

DLambertW0(x) =
/* ----------------------------------------------------------
 Computes the derivative of the upper branch of the
 Lambert W function. Intended for t_REAL arguments.
 Domain: (-1/e, +inf), Image: (-1, +inf)
---------------------------------------------------------- */
{
  my(c,eps,w);
  c = x + 0.0;
  eps = 10^(-default(realprecision)+1);
  if (type(c)!="t_REAL", error("Argument incompatible with t_REAL"));
  if (c <= eps-1.0/exp(1.0), error("Argument must be >= -1/e"));
  if (abs(c) < eps, return(1.0));
  w = LambertW0(c);
  return (w/(1.0+w)/c);
}

DLambertW1(x) =
/* ----------------------------------------------------------
 Computes the derivative of the lower branch of the
 Lambert W function. Intended for t_REAL arguments.
 Domain: (-1/e, 0), Image: (-1, -inf)
---------------------------------------------------------- */
{
  my(c,eps,w);
  c = x + 0.0;
  eps = 10^(-default(realprecision)+1);
  if (type(c)!="t_REAL", error("Argument incompatible with t_REAL"));
  if (c >=0, error("Argument must be < 0"));
  if (c <= eps-1.0/exp(1.0), error("Argument must be >= -1/e"));
  w = LambertW1(c);
  return (w/(1.0+w)/c);
}

ADLambertW0(x) =
/* ----------------------------------------------------------
 Computes the anti-derivative of the upper branch of the
 Lambert W function. Intended for t_REAL arguments.
 Domain: (-1/e, +inf), Image: (-1, +inf)
---------------------------------------------------------- */
{
  my(c,eps,w);
  c = x + 0.0;
  eps = 10^(-default(realprecision)+1);
  if (type(c)!="t_REAL", error("Argument incompatible with t_REAL"));
  if (c <= eps-1.0/exp(1.0), error("Argument must be >= -1/e"));
  if (abs(c) < eps, return(0.0));
  w = LambertW0(c);
  return (c*(w-1.0+1.0/w));
}

ADLambertW1(x) =
/* ----------------------------------------------------------
 Computes the anti-derivative of the lower branch of the
 Lambert W function. Intended for t_REAL arguments.
 Domain: (-1/e, 0), Image: (-1, -inf)
---------------------------------------------------------- */
{
  my(c,eps,w);
  c = x + 0.0;
  eps = 10^(-default(realprecision)+1);
  if (type(c)!="t_REAL", error("Argument incompatible with t_REAL"));
  if (c >=0, error("Argument must be < 0"));
  if (c <= eps-1.0/exp(1.0), error("Argument must be >= -1/e"));
  w = LambertW1(c);
  return (c*(w-1.0+1.0/w));
}


/*
=============================================================
   References:
     http://en.wikipedia.org/wiki/Lambert_W_function

   Contributed to OEIS Wiki by Stanislav Sykora
=============================================================
*/
